package com.atguigu.gmall.gateway;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.common.result.ResultCodeEnum;
import com.atguigu.gmall.common.util.IpUtil;
import lombok.SneakyThrows;
import org.apache.http.HttpHeaders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.util.Map;

@Component
public class GmallGatewayFilter implements GlobalFilter {

    @Value("${gmall.login}")
    String urls;
    @Autowired
    RedisTemplate redisTemplate;

    final private AntPathMatcher antPathMatcher = new AntPathMatcher();

    @SneakyThrows
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        String url = request.getURI().getPath();

        //  block inner in url
        boolean match = antPathMatcher.match("**/inner/**", url);
        if (match) {
            return out(response, ResultCodeEnum.PERMISSION);
        }
        //获取临时id或者用户id
        String tempUserId = getTempUserId(request);
        String userId = getUserId(request);
        if("-1".equals(userId)){
            //  信息提示：  209 没有权限访问
            return out(response, ResultCodeEnum.PERMISSION);
        }

        //  用户是否访问了 带有 /api/**/auth/** 数据接口，如果访问了，则要限制
        if (antPathMatcher.match("/api/**/auth/**",url)){
            //  判断用户是否登录 ，如果没有登录，则提示信息，没有权限的信息！
            if (StringUtils.isEmpty(userId)){
                //  信息提示：  208 未登陆
                return out(response, ResultCodeEnum.LOGIN_AUTH);
            }
        }



        //yml里配置的gmall.login拦截的页面需要登陆才能访问,重定向到登录页面,并且附上原网址callback
        if (StringUtils.isEmpty(userId)){
            if (urls != null) {
                String[] split = urls.split(":");
                for (String s : split) {
                    if (url.contains(s)) {
                        return forward(response,"http://passport.gmall.com/login.html?originUrl="+request.getURI());
                    }
                }
            }
        }


        //向后传递userid
        if (StringUtils.isEmpty(tempUserId) && StringUtils.isEmpty(userId)) {
            return chain.filter(exchange);
        }
        ServerHttpRequest build = request.mutate()
                                         .header("tempUserId", tempUserId)
                                         .header("userId", userId)
                                         .build();
        return chain.filter(exchange.mutate().request(build).build());


    }

    private Mono<Void> out(ServerHttpResponse response, ResultCodeEnum resultCodeEnum) {
        //  将用户的提示消息直接输入到页面！ 用户提示消息 resultCodeEnum
        Result<Object> result = Result.build(null, resultCodeEnum);
        //  将这个返回结果变成字符串
        //  如果不设置字符，防止出现字符集乱码可以使用
        //  String str = JSON.toJSONString(result).getBytes("utf-8");
        String str = JSON.toJSONString(result);
        DataBuffer wrap = response.bufferFactory().wrap(str.getBytes());
        //  向页面输入内容的时候，需要设置排头 "Content-Type", "application/json;charset=UTF-8"
        response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        //  直接将数据输入到页面！
        return response.writeWith(Mono.just(wrap));
    }

    private Mono<Void> forward(ServerHttpResponse response, String url) {
        //http 303 see other
        response.setStatusCode(HttpStatus.FOUND);
        response.setStatusCode(HttpStatus.SEE_OTHER);
        response.getHeaders().set(HttpHeaders.LOCATION, url);
        return response.setComplete();
    }

    /**
     * 从header或cookie里取出token,查询redis获取userid
     * @param request
     * @return null未找到,-1 token盗用,正常返回userid
     */
    private String getUserId(ServerHttpRequest request) {
        MultiValueMap<String, HttpCookie> cookies = request.getCookies();
        org.springframework.http.HttpHeaders headers = request.getHeaders();

        String token = null;
        HttpCookie tokenCookie = cookies.getFirst("token");
        if (tokenCookie != null) {
            token = tokenCookie.getValue();
        }

        String token2 = headers.getFirst("token");
        if (!StringUtils.isEmpty(token2)) {
            token = token2;
        }

        if (token!=null){
            String userKey = "user:login:" + token;

            String userJson = (String) redisTemplate.opsForValue().get(userKey);
            //  这个字符串对应的是哪个类 {JSONObject}
            JSONObject jsonObject = JSONObject.parseObject(userJson);


            if (jsonObject==null){
                //缓存里没有ip和userid
                return null;
            }else {
                String ip = jsonObject.get("ip").toString();
                String userId = jsonObject.get("userId").toString();

                if (ip.equals(IpUtil.getGatwayIpAddress(request))){
                    return userId;
                }else {
                    return "-1";
                }

            }

        }

        //cookie和header里都没token
        return null;

    }
    private String getTempUserId(ServerHttpRequest request) {
        MultiValueMap<String, HttpCookie> cookies = request.getCookies();
        org.springframework.http.HttpHeaders headers = request.getHeaders();

        String userId = null;
        HttpCookie tokenCookie = cookies.getFirst("userId");
        if (tokenCookie != null) {
            userId = tokenCookie.getValue();
        }

        String tokenHeader = headers.getFirst("userId");
        if (!StringUtils.isEmpty(tokenHeader)) {
            userId = tokenHeader;
        }
        return userId;
    }
}
